﻿///<copyright>
/// Copyright © 2009 Stephen Styrchak. All Rights Reserved.
///</copyright>
using System;
using System.ComponentModel.Design;
using Ferpect.ComponentModel.Menus;
using Ferpect.ComponentModel.Sprites;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Ferpect.ComponentModel.Design
{
    public class SelectionRectangle
    {
        private Vector3[] borderVertices;
        private Vector2[] handleVertices;
        private Vector2[] handlePositions;
        private Vector2 twistHandlePosition;
        private VertexPositionColor[] border;
        private GraphicsDevice graphicsDevice;
        private Sprite sprite;
        private Texture2D resizeHandle;
        private Texture2D twistHandle;
        private EmbeddedContentManager content;
        private SpriteBatch spriteBatch;
        private Matrix transform;
        private float rotation;
        private float scale;
        private Rectangle? grabHandle;
        private IComponentChangeService componentService;
        private Matrix hitTransform;

        public SelectionRectangle(IServiceProvider serviceProvider)
        {
            if (serviceProvider == null) throw new ArgumentNullException("serviceProvider");

            IGraphicsDeviceService graphicsService = (IGraphicsDeviceService)serviceProvider.GetService(typeof(IGraphicsDeviceService));

            this.graphicsDevice = graphicsService.GraphicsDevice;
            this.spriteBatch = new SpriteBatch(graphicsDevice);

            this.borderVertices = new Vector3[] {
                new Vector3(0, 0, 0),
                new Vector3(1, 0, 0),
                new Vector3(1, 0, 0),
                new Vector3(1, 1, 0),
                new Vector3(1, 1, 0),
                new Vector3(0, 1, 0),
                new Vector3(0, 1, 0),
                new Vector3(0, 0, 0),
            };

            this.handleVertices = new Vector2[] {
                new Vector2(1, 1),      // scale either direction
                new Vector2(1, 0.5f),   // scale X direction
                new Vector2(0.5f, 1),   // scale Y direction
            };

            this.handlePositions = new Vector2[this.handleVertices.Length];

            this.border = new VertexPositionColor[this.borderVertices.Length];

            this.content = new EmbeddedContentManager(serviceProvider, this.GetType().Assembly, this.GetType().Namespace + ".EmbeddedContent");
            this.resizeHandle = this.content.Load<Texture2D>("ResizeHandle");
            this.twistHandle = this.content.Load<Texture2D>("TwistHandle");
            Color selectionColor = Color.White;
            selectionColor.A = 100;
            this.Color = selectionColor;

            this.componentService = (IComponentChangeService)serviceProvider.GetService(typeof(IComponentChangeService));
            this.componentService.ComponentChanged += this.OnComponentChanged;
        }

        private void OnComponentChanged(object sender, ComponentChangedEventArgs args)
        {
            if (args.Component == this.sprite)
            {
                this.UpdateSprite();
            }
        }

        public Color Color
        {
            get;
            set;
        }

        public Rectangle? GrabHandle
        {
            get
            {
                return this.grabHandle;
            }
        }

        public Sprite Sprite
        {
            get
            {
                return this.sprite;
            }
            set
            {
                this.sprite = value;
                this.UpdateSprite();
            }
        }

        private void UpdateSprite()
        {
            if (this.sprite != null)
            {
                Vector2 size = this.sprite.GetSize();
                Vector2 position = this.sprite.Position;
                Vector2 origin = this.sprite.Origin;
                this.rotation = this.sprite.Rotation;
                this.scale = this.sprite.Scale;

                Update(ref size, ref position, ref origin);
                this.UpdateHitTransform();
            }
        }

        public bool GetHitTest(System.Drawing.Point clientPt)
        {
            if (this.sprite != null)
            {
                Vector2 point = new Vector2(clientPt.X, clientPt.Y);
                point = Vector2.Transform(point, this.hitTransform);

                Vector2 size = this.sprite.GetSize();
                Rectangle boundingRect = new Rectangle(0, 0, (int)size.X, (int)size.Y);
                return boundingRect.Contains((int)point.X, (int)point.Y);
            }
            return false;
        }

        private void UpdateHitTransform()
        {
            Matrix translate = Matrix.CreateTranslation(-this.sprite.Position.X, -this.sprite.Position.Y, 0);
            Matrix scale = Matrix.CreateScale(1f / this.sprite.Scale, 1f / this.sprite.Scale, 1);
            Matrix rotate = Matrix.CreateRotationZ(-this.sprite.Rotation);
            Matrix retranslate = Matrix.CreateTranslation(this.sprite.Origin.X, this.sprite.Origin.Y, 0);
            this.hitTransform = translate * scale * rotate * retranslate;
        }

        public void Update()
        {
            if (this.sprite != null)
            {
                this.UpdateSprite();
            }
        }

        private void Update(ref Vector2 size, ref Vector2 position, ref Vector2 origin)
        {
            float padding = 4f / this.scale;
            float offset = 2f / this.scale;
            this.transform = Matrix.CreateScale(size.X + padding, size.Y + padding, 1) * Matrix.CreateTranslation(-origin.X - offset, -origin.Y - offset, 0) * Matrix.CreateRotationZ(this.rotation) * Matrix.CreateScale(this.scale) * Matrix.CreateTranslation(position.X, position.Y, 0);

            for (int i = 0; i < this.borderVertices.Length; ++i)
            {
                this.border[i].Position = Vector3.Transform(this.borderVertices[i], this.transform);
                this.border[i].Color = this.Color;
            }

            this.twistHandlePosition = Vector2.Transform(new Vector2(0, 0), this.transform);
            this.grabHandle = new Rectangle((int)this.twistHandlePosition.X - 4, (int)this.twistHandlePosition.Y - 4, 8, 8);

            for (int i = 0; i < this.handleVertices.Length; ++i)
            {
                this.handlePositions[i] = Vector2.Transform(this.handleVertices[i], this.transform);
            }
        }

        public void Draw()
        {
            if (this.sprite != null)
            {
                // set up a new basic effect, and enable vertex colors.
                BasicEffect basicEffect = new BasicEffect(graphicsDevice);
                basicEffect.VertexColorEnabled = true;

                // projection uses CreateOrthographicOffCenter to create 2d projection
                // matrix with 0,0 in the upper left.
                basicEffect.Projection = Matrix.CreateOrthographicOffCenter(0, graphicsDevice.Viewport.Width, graphicsDevice.Viewport.Height, 0, 0, 1);

                RasterizerState rasterizerState = new RasterizerState();
                rasterizerState.CullMode = CullMode.None;

                graphicsDevice.RasterizerState = rasterizerState;
                foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
                {
                    pass.Apply();
                    // Draw the lines
                    graphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.LineList, this.border, 0, this.border.Length / 2, VertexPositionColor.VertexDeclaration);
                }

                this.spriteBatch.Begin();

                Color handleColor = this.Color;
                handleColor.A = 255;
                this.spriteBatch.Draw(this.twistHandle, this.twistHandlePosition, null, handleColor, 0, new Vector2(4, 4), 1, SpriteEffects.None, 0);

                //for (int i = 0; i < this.handleVertices.Length; ++i)
                //{
                //    this.spriteBatch.Draw(this.resizeHandle, this.handlePositions[i], null, handleColor, this.rotation, new Vector2(1, 1), 3, SpriteEffects.None, 0);
                //}

                this.spriteBatch.End();
            }
        }
    }
}
